package com.idea.relax.crypto.core;

import com.idea.relax.crypto.annotation.Decrypt;
import com.idea.relax.crypto.config.CryptoType;
import com.idea.relax.crypto.config.RelaxCryptoProperties;
import com.idea.relax.crypto.exception.CryptoException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
 * @author: 沉香
 * @date: 2023/3/17
 * @description:
 */
@Slf4j
@Order(1)
@AutoConfiguration
@ControllerAdvice
@AllArgsConstructor
@ConditionalOnProperty(prefix = RelaxCryptoProperties.PREFIX, name = ".enabled", havingValue = "true")
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

	private final RelaxCryptoProperties properties;

	private final ICryptographerRegister cryptographerRegister;


	@Override
	public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		if (!properties.isEnabled()) {
			return false;
		}
		return AnnotatedElementUtil.isAnnotated(methodParameter, Decrypt.class);
	}

	@Override
	public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
		InputStream body = inputMessage.getBody();
		//没有请求数据不做处理
		if (body.available() <= 0) {
			return inputMessage;
		}
		//获取配置信息
		String privateKey = properties.getPrivateKey();
		//获取方法或者类上的注解
		Decrypt decrypt = AnnotatedElementUtil.getAnnotation(parameter, Decrypt.class);
		String annoPriKey = decrypt.privateKey();
		CryptoType cryptoType = decrypt.cryptoType();
		//如果注解上未指定私钥，则使用配置类中的
		if (Func.isNotEmpty(annoPriKey)) {
			privateKey = annoPriKey;
		}
		if (Func.isEmpty(privateKey)) {
			throw new CryptoException("Configure a private key!");
		}
		//找出复合条件的ICryptographer加密类
		ICryptographer cryptographer = cryptographerRegister.getIfAbsentThrow(cryptoType);
		//将请求体转为byte字节
		byte[] bodyByteArray = StreamUtils.copyToByteArray(body);
		byte[] decryptedBody = cryptographer.decryptFormBase64(bodyByteArray, privateKey);
		InputStream inputStream = new ByteArrayInputStream(decryptedBody);
		return new DecryptHttpInputMessage(inputStream, inputMessage.getHeaders());
	}


	@Override
	public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		return body;
	}

	@Override
	public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
		return body;
	}


}
